﻿namespace NTLib.SandBox
{
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Security;
    using System.Text;
    using System.Threading.Tasks;

    [TestClass]
    public class ReflectionTypeUTest
    {
        #region Nested
        
        class ObjectTest
        {
            public string Name { get; set; }
        }

        #endregion

        #region Methods

        [TestCategory("SandBox")]
        [TestMethod]
        public void CacheProperty()
        {
            var zero = new object[0];
            string n = null;

            double count = 1000000.0d;
            ObjectTest t = new ObjectTest();
            Stopwatch watch = new Stopwatch();

            watch.Reset();
            watch.Start();

            for (int i = 0; i < count; ++i)
            {
                n = t.Name;
            }

            Trace.WriteLine("Property Direct Call " + watch.ElapsedMilliseconds / count);


            watch.Reset();
            watch.Start();

            for (int i = 0; i < count; ++i)
            {
                var mthInfo = typeof(ObjectTest).GetProperty("Name");
                n = (string)mthInfo.GetValue(t);
            }

            Trace.WriteLine("Property Reflection each Call " + watch.ElapsedMilliseconds / count);
            
            watch.Reset();
            watch.Start();

            var mthInfoS = typeof(ObjectTest).GetProperty("Name");
            for (int i = 0; i < count; ++i)
                n = (string)mthInfoS.GetValue(t);

            Trace.WriteLine("Property Reflection cached Call " + watch.ElapsedMilliseconds / count);

            watch.Reset();
            watch.Start();

            for (int i = 0; i < count; ++i)
                n = (string)mthInfoS.GetMethod.Invoke(t, zero);


            Trace.WriteLine("Property Reflection cached Mth Call " + watch.ElapsedMilliseconds / count);
            
        }

        [TestCategory("SandBox")]
        [TestMethod]
        public void CacheMethod()
        {
            var zero = new object[0];
            double count = 1000000.0d;
            ObjectTest t = new ObjectTest();
            Stopwatch watch = new Stopwatch();

            watch.Reset();
            watch.Start();

            for (int i = 0; i < count; ++i)
                t.ToString();

            Trace.WriteLine("Direct Call " + watch.ElapsedMilliseconds / count);


            watch.Reset();
            watch.Start();

            for (int i = 0; i < count; ++i)
            {
                var mthInfo = typeof(ObjectTest).GetMethod("ToString");
                mthInfo.Invoke(t, zero);
            }

            Trace.WriteLine("Reflection each Call " + watch.ElapsedMilliseconds / count);
            
            watch.Reset();

            var mthInfoS = typeof(ObjectTest).GetMethod("ToString");

            watch.Start();

            for (int i = 0; i < count; ++i)
                mthInfoS.Invoke(t, zero);

            Trace.WriteLine("Reflection cached Call " + watch.ElapsedMilliseconds / count);

            var mthInfoType = mthInfoS.GetType();
            var p = mthInfoType.GetField("m_signature", BindingFlags.Instance | BindingFlags.NonPublic);
            var sign = p.GetValue(mthInfoS);

            var rType = typeof(RuntimeMethodHandle);

            var mth = rType.GetMethod("InvokeMethod", BindingFlags.NonPublic | BindingFlags.Static);

            var par = new object[] { t, zero, sign, false };

            watch.Reset();
            watch.Start();

            for (int i = 0; i < count; ++i)
                mth.Invoke(null, par);

            Trace.WriteLine("Call sub runtime Call " + watch.ElapsedMilliseconds / count);

        }

        #endregion
    }
}
